package v5

import (
	"bytes"
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/sha256"
	"github.com/btcsuite/btcutil/base58"
	"golang.org/x/crypto/ripemd160"
	"log"
)

type Wallet struct {
	//椭圆曲线的私钥
	//⾸字⺟⼀定要⼤写，否则后⾯gob编码时会出错(⽤于保存)
	/**
	在 Go 语言中，使用 gob:"-" 标签可以忽略结构体中的字段，但如果结构体是多层嵌套的（即结构体中包含其他结构体），
	gob:"-" 标签可能无法直接忽略嵌套结构体中的字段。这是因为 gob 包在序列化和反序列化时，会递归处理结构体的所有字段，包括嵌套结构体。
	*/
	PrivateKey *ecdsa.PrivateKey `json:"-" gob:"-"` // 忽略该字段 gob:"-",多个忽略用空格,这个gob:"-"在这是不会生效的,多层嵌套了
	//PublicKey
	//D *big.Int
	//这个是私钥里面的D,由于上面的序列化失败,设置为忽略,最后再做数据组装
	D []byte
	//可以通过X,Y坐标还原
	// PublicKey represents an ECDSA public key.
	//type PublicKey struct {
	//	elliptic.Curve
	//	X, Y *big.Int
	//}
	//对应的X,Y坐标的拼接作为公钥
	PublicKey []byte
}

func NewWallet() *Wallet {
	privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		log.Panic(err)
	}
	publicKey := privateKey.PublicKey
	//是X,Y拼接而来
	publicKeyBytes := append(publicKey.X.Bytes(), publicKey.Y.Bytes()...)
	return &Wallet{privateKey, privateKey.D.Bytes(), publicKeyBytes}
}

// 由公钥生成地址-->//1.地址生成流程.jpg
func (w *Wallet) GetAddress() string {
	//ripemd160Hash
	ripemd160SumPubKeyHash := GetRipemd160SumPubKeyHash(w.PublicKey) // 对应的X,Y坐标的拼接作为公钥
	pubKey21Hash := append([]byte{byte(00)}, ripemd160SumPubKeyHash...)
	checksum := GetChecksum(pubKey21Hash)
	bytes25 := append(pubKey21Hash, checksum...)
	Address := base58.Encode(bytes25)
	return Address
}

func GetChecksum(ripemd160 []byte) []byte {
	sum256 := sha256.Sum256(ripemd160)
	sum256Two := sha256.Sum256(sum256[:])
	return sum256Two[:4]
}

func GetRipemd160SumPubKeyHash(publicKey []byte) []byte {
	sum256 := sha256.Sum256(publicKey)
	ripemd160Hasher := ripemd160.New()
	ripemd160Hasher.Write(sum256[:])
	ripemd160SumPubKeyHash := ripemd160Hasher.Sum(nil)
	return ripemd160SumPubKeyHash
}

// 基础校验地址是否正确
func IsValidAddress(address string) bool {
	//1. 逆向求出pubKeyHash数据
	//2. 得到version + 哈希1 部分，并做checksum运算
	//3. 与实际checksum⽐较，如果相同，则地址有效，反之则⽆效
	pubKeyHash := base58.Decode(address)
	//防⽌输⼊的过短引起下⾯访问越界
	if len(pubKeyHash) < 4 {
		return false
	}
	payload := pubKeyHash[:len(pubKeyHash)-4]
	actualCheckSum := pubKeyHash[len(pubKeyHash)-4:]
	targetCheckSum := GetChecksum(payload)
	return bytes.Compare(actualCheckSum, targetCheckSum) == 0
}

// 根据地址获取公钥hash
func GetPubKeyHashByAddress(address string) []byte {
	//1. 逆向求出pubKeyHash数据
	//2. 得到version + 哈希1 部分，并做checksum运算
	//3. 与实际checksum⽐较，如果相同，则地址有效，反之则⽆效
	pubKeyHash25 := base58.Decode(address)
	//防⽌输⼊的过短引起下⾯访问越界
	if len(pubKeyHash25) < 5 {
		return nil
	}
	return pubKeyHash25[1 : len(pubKeyHash25)-4]
}
